﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Threading;

public enum EffectStep {
	
	NAME,
	
	BEGIN,
	
	BULLET,
	
	END,
	
	COMPLETE,
	
	AllCOMPLETE,
}

public class EffectStepObject {
	
	public int index;
	
	public GameObject target;
	
	public EffectStep effectStep = EffectStep.BEGIN;
	
	public GameObject beginObject;
	
	public GameObject bulletObject;
	
	public GameObject endObject;
	
	public CallBack stepComplete;
	
	public void complete() {
		stepComplete();
	}
}

public enum EffectBullet {

	Normal,

	Remote,

	Direct,
}

public enum EffectReverse {

	None,

	All,

	Bullet,

	End,
}

public abstract class AniEffectSkill : AniEffectGroup {
	
	public GameObject nameObject;
	public GameObject beginObject;
	public GameObject bulletObject;
	public GameObject endObject;
	
	public AudioClip nameClip;
	public AudioClip beginClip;
	public AudioClip bulletClip;
	public AudioClip endClip;
	
	// skill name
	public string name;
	
	// skill name time
	public float nameSecend;

	// skill complete time
	public float completeSecend;
	
	// bullet interval delay time
	public float delaySecend;
	
	// bullet time
	public float bulletSecend = 0.2f;
	
	// is remote attack
	public EffectBullet bullet = EffectBullet.Remote;

	// is reverse attack
	public EffectReverse reverse = EffectReverse.None;
	
	// self gameObject
	protected GameObject self;

	// targets gameObject
	protected GameObject[] targets;
	
	// effect data
	private object effectData;

	// effect data proccessed
	private bool effectProccessed;

	// effect name default secend
	public static float NameDelaySecend = 0.15f;

	private AniEffectSkillA[] aniEffectSkillAs;
	
	public override void play (OReportDetail detail, CallBack complete) {
		if(aniEffectSkillAs == null) {
			aniEffectSkillAs = gameObject.GetComponents<AniEffectSkillA>();
		}

		this.detail = detail;
		this.complete = complete;
		self = OReportEffect.forGameObject(detail.self);
		if(self == null) {
			completeSecend = 0;
			playCompleteAll();
			
		} else {
			VieworUtils.addView(transform, self.transform.parent);
			if(completeSecend <= 0) {
				completeSecend = 1;
				
			} else {
				float delaySecond = completeSecend * AniBase.Speed;
				completeSecend = 2;
				StartCoroutine(GameMonoUtils.getDelayCallBack(delaySecond, ()=>{--completeSecend; playCompleteAll();}));
			}

			playName();
		}
	}
	
	public virtual void playName() {
		if(nameClip != null) {
			NGUITools.PlaySound(nameClip);
		}

		if(nameObject == null) {
			playGroup();

		} else {
			VieworUtils.addView(nameObject.transform, self.transform);
			nameObject.SetActive(true);
			CallBack callBack = () => {Destroy(nameObject); playGroup();};
			if(nameSecend <= 0) {
				AniBase aniBase = nameObject.GetComponentInChildren<AniBase>();
				if(aniBase != null) {
					aniBase.playCallBack(callBack);
					return;
				}
				
				nameSecend = NameDelaySecend + NameDelaySecend;
			}

			StartCoroutine(GameMonoUtils.getDelayCallBack(nameSecend * AniBase.Speed, callBack));
		}
	}

	protected override bool playGroup(AniEffectC aniEffectC) {
		if(aniEffectC is AniEffectAniSelf) {
			((AniEffectAniSelf)aniEffectC).self = self;
		}

		return true;
	}
	
	public override void playComplete() {
		playSkill();
	}

	public virtual void playCompleteAll() {
		if(completeSecend == 0) {
			completeSecend = 1;
			base.playComplete();
			Destroy(gameObject);
		}
	}

	private void playSkill() {
		int[] targets = detail.targets;
		int length = targets == null ? 0 : targets.Length;
		if(length == 0) {
			completeSecend = 0;
			playCompleteAll();
			
		} else {
			StartCoroutine(playSkillEnumerator());
		}
	}

	public virtual IEnumerator playSkillEnumerator() {
		int[] gIds = detail.targets;
		int length = gIds.Length;
		targets = new GameObject[length];

		for(int i = 0; i < length; i++) {
			if(delaySecend > 0 && i > 0) {
				yield return new WaitForSeconds(delaySecend);
			}

			GameObject target = OReportEffect.forGameObject(gIds[i]);
			if(target != null) {
				++completeSecend;
				targets[i] = target;
				EffectStepObject effectStepObject = new EffectStepObject();
				effectStepObject.index = i;
				effectStepObject.target = target;
				effectStepObject.stepComplete = getStepComplete(effectStepObject);
				playStep(effectStepObject);
			}
		}

		--completeSecend;
	}

	protected CallBack getStepComplete(EffectStepObject effectStepObject) {
		return ()=>{playStep(effectStepObject);};
	}

	public void playStep (EffectStepObject effectStepObject) {
		// Debug.Log("playStep:" + effectStepObject + ":" + effectStepObject.effectStep + "=>" + effectStepObject.index);
		bool overrideFlag = false;
		switch(effectStepObject.effectStep) {
		case EffectStep.BEGIN:
			effectStepObject.effectStep = EffectStep.BULLET;
			foreach(AniEffectSkillA aniEffectSkillA in aniEffectSkillAs) {
				overrideFlag = aniEffectSkillA.playBegan(this, effectStepObject, overrideFlag);
			}
			
			if(!overrideFlag) {
				playBegan(effectStepObject);
			}

			break;
		case EffectStep.BULLET:
			effectStepObject.effectStep = EffectStep.END;
			foreach(AniEffectSkillA aniEffectSkillA in aniEffectSkillAs) {
				overrideFlag = aniEffectSkillA.playBullet(this, effectStepObject, overrideFlag);
			}
			
			if(!overrideFlag) {
				playBullet(effectStepObject);
			}

			break;
		case EffectStep.END:
			effectStepObject.effectStep = EffectStep.COMPLETE;
			foreach(AniEffectSkillA aniEffectSkillA in aniEffectSkillAs) {
				overrideFlag = aniEffectSkillA.playEnd(this, effectStepObject, overrideFlag);
			}
			
			if(!overrideFlag) {
				playEnd(effectStepObject);
			}

			break;
		case EffectStep.COMPLETE:
			effectStepObject.effectStep = EffectStep.AllCOMPLETE;
			foreach(AniEffectSkillA aniEffectSkillA in aniEffectSkillAs) {
				overrideFlag = aniEffectSkillA.playComplete(this, effectStepObject, overrideFlag);
			}
			
			if(!overrideFlag) {
				playComplete(effectStepObject);
			}

			break;
		default:
			--completeSecend;
			playCompleteAll();
			break;
		};
	}

	public virtual void playBegan(EffectStepObject effectStepObject) {
		// Debug.Log("playBegan:" + effectStepObject.index + ":" + effectStepObject.target);
		if(beginObject == null) {
			effectStepObject.complete();

		} else {
			effectStepObject.beginObject = effectObject(beginObject, self, effectStepObject.stepComplete);
		}

		if(beginClip != null) {
			NGUITools.PlaySound(beginClip);
		}
	}
	
	public virtual void playBullet(EffectStepObject effectStepObject) {
		// Debug.Log("playBullet:" + effectStepObject.index + ":" + effectStepObject.target);
		if(effectStepObject.beginObject != null) {
			Destroy(effectStepObject.beginObject);
		}

		if(bulletObject == null) {
			effectStepObject.complete();

		} else {
			bool target = reverse == EffectReverse.All || reverse == EffectReverse.Bullet;
			effectStepObject.bulletObject = effectObject(bulletObject, target ? effectStepObject.target : self, bullet == EffectBullet.Remote ? null : effectStepObject.stepComplete);
			if(bullet != EffectBullet.Normal) {
				Transform [] path = target ? new Transform[2]{effectStepObject.target.transform, self.transform} : new Transform[2]{self.transform, effectStepObject.target.transform};
				float angle = Mathf.Atan2(path[0].position.x - path[1].position.x, path[1].position.y - path[0].position.y)*180/3.14f;
				effectStepObject.bulletObject.transform.localEulerAngles = new Vector3(0, 0, angle);
				if(bullet == EffectBullet.Remote) {
					iTween.MoveTo(effectStepObject.bulletObject, iTween.Hash("path", path, "time", bulletSecend * AniBase.Speed, "easetype", iTween.EaseType.easeInOutCubic, "oncompletetarget", gameObject, "oncomplete", "playStep", "oncompleteparams", effectStepObject));
				}
			}
		}

		if(bulletClip != null) {
			NGUITools.PlaySound(bulletClip);
		}
	}
	
	public virtual void playEnd(EffectStepObject effectStepObject) {
		// Debug.Log("playEnd:" + effectStepObject.index + ":" + effectStepObject.target);
		if(effectStepObject.bulletObject != null) {
			Destroy(effectStepObject.bulletObject);
		}
		
		if(endObject == null) {
			effectStepObject.complete();

		} else {
			effectStepObject.endObject = effectObject(endObject, reverse == EffectReverse.All || reverse == EffectReverse.End ? self : effectStepObject.target, effectStepObject.stepComplete);
		}

		if(endClip != null) {
			NGUITools.PlaySound(endClip);
		}
	}

	public virtual object getEffectData() {
		if(!effectProccessed) {
			effectProccessed = true;
			if(detail.effectData != null) {
				try {
					effectData = proccessEffectData();
					
				} catch(System.Exception e) {
					effectData = null;
				}
			}
		}

		return effectData;
	}

	protected virtual object proccessEffectData() {
		return JsonFx.Json.JsonReader.Deserialize(UnityORM.Json.Serialize(detail.effectData), typeof(List<OReportDetail[]>));
	}

	public virtual void playComplete(EffectStepObject effectStepObject) {
		// Debug.Log("playComplete:" + effectStepObject.index);
		if(effectStepObject.endObject != null) {
			Destroy(effectStepObject.endObject);
		}

		OReportDetail[][] allDetails = getEffectData() as OReportDetail[][];
		if(allDetails == null || effectStepObject.index >= allDetails.Length) {
			effectStepObject.complete();
			
		} else {
			OReportEffect.playDetails(allDetails[effectStepObject.index], effectStepObject.complete);
		}
	}
}
